package com.wsc.it.parking;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;

import org.apache.http.HttpStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.Response;
import com.squareup.okhttp.ResponseBody;
import com.wsc.it.parking.enums.FujicaInterfaceEnum;
import com.wsc.it.parking.vo.DistributeDataResponse;
import com.wsc.it.parking.vo.FujicaBaseResponse;
import com.wsc.it.parking.vo.MonthCardInfoResponse;
import com.wsc.it.parking.vo.ParkingLotListResponse;
import com.wsc.it.parking.vo.ParkingSpaceResponse;
import com.wsc.it.parking.vo.TempCarPaymentInfoResponse;
import com.wsc.it.parking.vo.UnlicensedCarsOutInfoResponse;
import com.wsc.it.parking.vo.ValueCardInfoResponse;
import com.wsc.it.utils.Base64;
import com.wsc.it.utils.DateUtils;
import com.wsc.it.utils.PrinterUtil;
import com.wsc.it.utils.StringUtil;

import lombok.extern.log4j.Log4j2;

/**
 * @description: 富士停车接口服务
 * @author: wsc58888
 * @create: 2022-02-07
 **/
@Log4j2
public class FujicaApiService {
	private static final Logger log = LoggerFactory.getLogger(PrinterUtil.class);

	private static final String ALGORITHM = "RSA";
	private static final String SIGN_ALGORITHMS = "SHA1WithRSA";

	private static final String DEFAULT_CHARSET = "UTF-8";
	public static final MediaType MEDIATYPE_JSON = MediaType.parse("application/json; charset=utf-8");
	//物业编码:
	private static String propertyCode = "111111111111111111111111111111";
	//appID:
	private static String appid = "111111111111111111111111111111";
	//appID密钥：
	private static String appSecret = "111111111111111111111111111111";
	private static String sign = "";
	private static String time;

	private static String privateKey = "";

	
	
	
/**
 * 
 * 
	public static void main(String[] args) {
		//粤 
		//粤 
		//虚 
		//虚 
		
		String requestJson41 = "{'ParkingCode': '20000000000000','CarNo': '虚LPCTJR'}";
		FujicaBaseResponse parkResponse41 = FujicaApiService.getParkInfoByCarNo(requestJson41);
		log.info("4.1获取停车信息（通过车牌号）",JSON.toJSONString(parkResponse41));
		
		String requestJson42 = "{'ParkingCode': '20000000000000','CardNo': '虚LPCTJR'}";
		FujicaBaseResponse parkResponse42 = FujicaApiService.getParkInfoByCardNo(requestJson42);
		log.info("4.2获取停车信息（通过卡号）",JSON.toJSONString(parkResponse42));
	
		String requestJson43 = "{'ParkingCode': '20000000000000','CarNo': '虚LPCTJR'}";
		FujicaBaseResponse parkResponse43 = FujicaApiService.getTempCarPaymentInfoByCarNo(requestJson43);
		log.info("4.3获取临时车信息（通过车牌号）返回信息：{}",JSON.toJSONString(parkResponse43));
		
		String requestJson492 = "{'ParkingCode': '20000000000000','WatchhouseCode': '008','CardNo': '虚LPCTJR'}";
		FujicaBaseResponse parkResponse492 = FujicaApiService.unlicensedCarsOut(requestJson492);
		log.info("4.9.2无牌车二维码出场 返回信息：{}",JSON.toJSONString(parkResponse492));
		
		
		
		
		//String parkingLotRequestJson="{'PropertyCode': '"+propertyCode+"'}";
		//FujicaBaseResponse<List<ParkingLotListResponse>> parkingLotList = apiService
		//		.apiGetParkingLotListByPropertyCode(parkingLotRequestJson);
		//log.info(" 4.11.1通过物业编码获取停车场列表返回信息：{}",JSON.toJSONString(parkingLotList));
	
		//String parkingCodeRequestJson="{'ParkingCode': '20000000000000'}";
		//FujicaBaseResponse<ParkingLotListResponse> parkingLot = FujicaApiService.apiGetParkingLotInfo(parkingCodeRequestJson);
		//log.info("4.11.2获取单个停车场信息）返回信息：{}",JSON.toJSONString(parkingLot));
		
		 
		//FujicaBaseResponse<ParkingSpaceResponse> parkingSpace= 	FujicaApiService.apiGetParkingSpace(requestJson);
		//log.info("4.21获取车位信息（通过车场编码和车牌号）返回信息：{}",JSON.toJSONString(parkingSpace));
		
		//String distributeDataRequst ="{'ParkingCode': '20000000000000','ExportCode': '008'}";
		//FujicaBaseResponse<DistributeDataResponse> distributeDataResponse= 	FujicaApiService.apiGetDistributeData(distributeDataRequst);
		//log.info("4.20获取停车信息（通过车道码）返回信息：{}",JSON.toJSONString(distributeDataResponse));
		
	}

 */	
	
	/** 4.1获取停车信息（通过车牌号）*/
	public static  FujicaBaseResponse getParkInfoByCarNo(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.GETPARKINFOBYCARNO, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse>() {
		}.getType());
	}
	
	/** 4.2获取停车信息（通过卡号）*/
	public static FujicaBaseResponse getParkInfoByCardNo(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.GETPARKINFOBYCARDNO, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse>() {
		}.getType());
	}
	
	/** 4.3获取临时车信息（通过车牌号）*/
	public static FujicaBaseResponse<TempCarPaymentInfoResponse> getTempCarPaymentInfoByCarNo(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.GETTEMPCARPAYMENTINFOBYCARNO, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse<TempCarPaymentInfoResponse>>() {
		}.getType());
	}
	
	/** 4.4获取临时车信息（通过卡号）*/
	public static FujicaBaseResponse<TempCarPaymentInfoResponse> getTempCarPaymentInfoByCardNo(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.GETTEMPCARPAYMENTINFOBYCARDNO, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse<TempCarPaymentInfoResponse>>() {
		}.getType());
	}
	
	/** 4.5获取月租车信息（通过车牌号）*/
	public static FujicaBaseResponse<MonthCardInfoResponse> GetMonthCardInfoByCarNo(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.GETMONTHCARDINFOBYCARNO, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse<MonthCardInfoResponse>>() {
		}.getType());
	}
	
	/** 4.6获取月租车信息（通过卡号）*/
	public static FujicaBaseResponse<MonthCardInfoResponse> GetMonthCardInfoByCardNo(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.GETMONTHCARDINFOBYCARDNO, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse<MonthCardInfoResponse>>() {
		}.getType());
	}
	
	/** 4.7获取储值车信息（通过车牌号）*/
	public static FujicaBaseResponse<ValueCardInfoResponse> GetValueCardInfoByCarNo(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.GETVALUECARDINFOBYCARNO, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse<ValueCardInfoResponse>>() {
		}.getType());
	}
	
	/** 4.8获取储值车信息（通过卡号）*/
	public static FujicaBaseResponse<ValueCardInfoResponse> GetValueCardInfoByCardNo(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.GETVALUECARDINFOBYCARDNO, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse<ValueCardInfoResponse>>() {
		}.getType());
	}
	/** 4.9.1无牌车二维码入场*/
	public static FujicaBaseResponse unlicensedCarsIn(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.UNLICENSEDCARSIN, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse>() {
		}.getType());
	}
	
	/** 4.9.2无牌车二维码出场*/
	public static FujicaBaseResponse<UnlicensedCarsOutInfoResponse> unlicensedCarsOut(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.UNLICENSEDCARSOUT, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse<UnlicensedCarsOutInfoResponse>>() {
		}.getType());
	}
	
	/** 4.10.1第三方临时卡上发缴费
	 *
		{
		"Amount": 10.0,
		"ActualAmount": 10.0,
		"DeductionAmount": 0.0,
		"Reason": "",
		"DealNo": "2017082821001004930232739530",
		"PayStyle": "支付宝",
		"CarNo": "粤BS69W3",
		"ParkingCode": "17000107550001",
		"CardNo": "",
		"PayCostMachineOrderId": "",
		"CouponList": [
		    {
		"CouponType": 2,
		"CouponValue": 0.0,
		"CouponCode": "2319e5e516dd42f49a0514796fd0bb43",
		"FavorableType": 2,
		"IsAuto": 2,
		"UseStatus": 1
		    }
		  ]
		}

	 * */
	public static FujicaBaseResponse apiThirdPartyTemporaryCardPay(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.APITHIRDPARTYTEMPORARYCARDPAY, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse>() {
		}.getType());
	}
	
	/** 4.10.2第三方储值卡上发充值*/
	public static FujicaBaseResponse apiThirdPartyValueCardPay(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.APITHIRDPARTYVALUECARDPAY, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse>() {
		}.getType());
	}
	
	/** 4.10.3第三方月卡上发续费*/
	public static FujicaBaseResponse apiThirdPartyMonthCardPay(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.APITHIRDPARTYMONTHCARDPAY, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse>() {
		}.getType());
	}
	
	/** 4.10.4第三方支付验证
	 * 第三方支付验证，验证月卡储值卡下发是否成功
	 * */
	public static FujicaBaseResponse apiThirdPartyPayVerification(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.APITHIRDPARTYPAYVERIFICATION, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse>() {
		}.getType());
	}
	
	/** 4.11.1通过物业编码获取停车场列表*/
	public static FujicaBaseResponse<List<ParkingLotListResponse>> apiGetParkingLotListByPropertyCode(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.APIGETPARKINGLOTLISTBYPROPERTYCODE, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse<List<ParkingLotListResponse>>>() {
		}.getType());
	}

	/** 4.11.2获取单个停车场信息*/
	public static FujicaBaseResponse<ParkingLotListResponse>  apiGetParkingLotInfo(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.APIGETPARKINGLOTINFO, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse<ParkingLotListResponse>>() {
		}.getType());
	}
	
	/** 4.12.1给指定入场车辆分配优惠电子券(新)*/
	public static FujicaBaseResponse  assignElecoupon4Park(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.ASSIGNELECOUPON4PARK, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse>() {
		}.getType());
	}
	/** 4.12.2 创建并获取纸质优惠券列表*/
	public static FujicaBaseResponse  createPaperCouponsList(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.CREATEPAPERCOUPONSLIST, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse>() {
		}.getType());
	}
	
	/** 4.12.3 验证纸质优惠券是否有效并返回优惠券实体*/
	public static FujicaBaseResponse  verifyPaperCoupon(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.VERIFYPAPERCOUPON, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse>() {
		}.getType());
	}
	/** 4.12.4 修改优惠券使用状态*/
	public static FujicaBaseResponse  thirdpartyUseCoupons(String requestJson) {
		
		String json = invoke(FujicaInterfaceEnum.THIRDPARTYUSECOUPONS, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse>() {
		}.getType());
	}
	
	/** 4.14获取线下算费结果*/
	public static FujicaBaseResponse  apiGetOfflineFee(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.APIGETOFFLINEFEE, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse>() {
		}.getType());
	}
	
	/** 4.15带优惠券获取停车信息（通过车牌号）*/
	public static FujicaBaseResponse  getParkInfoByCarNoAndCalculateCoupon(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.GETPARKINFOBYCARNOANDCALCULATECOUPON, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse>() {
		}.getType());
	}
	
	/** 4.16带优惠券获取停车信息（通过卡号）*/
	public static FujicaBaseResponse  getParkInfoByCardNoAndCalculateCoupon(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.GETPARKINFOBYCARDNOANDCALCULATECOUPON, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse>() {
		}.getType());
	}
	
	/** 4.17带优惠券获取临时车信息（通过车牌号）*/
	public static FujicaBaseResponse<TempCarPaymentInfoResponse>  getTempCarPaymentInfoByCarNoAndCalculateCoupon(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.GETTEMPCARPAYMENTINFOBYCARNOANDCALCULATECOUPON, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse<TempCarPaymentInfoResponse>>() {
		}.getType());
	}
	
	/** 4.18带优惠券获取临时车信息（通过卡号）*/
	public static FujicaBaseResponse<TempCarPaymentInfoResponse>  getTempCarPaymentInfoByCardNoAndCalculateCoupon(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.GETTEMPCARPAYMENTINFOBYCARDNOANDCALCULATECOUPON, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse<TempCarPaymentInfoResponse>>() {
		}.getType());
	}
	
	
	/** 4.19.1锁车*/
	public static FujicaBaseResponse  apiLockCarByThirdParty(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.APILOCKCARBYTHIRDPARTY, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse>() {
		}.getType());
	}
	
	/** 4.19.2解锁*/
	public static FujicaBaseResponse  apiUnLockCarByThirdParty(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.APIUNLOCKCARBYTHIRDPARTY, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse>() {
		}.getType());
	}
	
	/** 4.20获取停车信息（通过车道码）*/
	public  static FujicaBaseResponse<DistributeDataResponse>  apiGetDistributeData(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.APIGETDISTRIBUTEDATA, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse<DistributeDataResponse>>() {
		}.getType());
	}
	
	/** 4.21获取车位信息（通过车场编码和车牌号）*/
	public static FujicaBaseResponse<ParkingSpaceResponse>  apiGetParkingSpace(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.APIGETPARKINGSPACE, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse<ParkingSpaceResponse>>() {
		}.getType());
	}
	
	/** 4.22车辆预约*/
	public static FujicaBaseResponse  apiVisitorCarAccredit(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.APIVISITORCARACCREDIT, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse>() {
		}.getType());
	}
	/** 4.23取消车辆预约*/
	public static FujicaBaseResponse  cancelCarAccredit(String requestJson) {
		String json = invoke(FujicaInterfaceEnum.CANCELCARACCREDIT, requestJson);
		return fromJson(json, new TypeToken<FujicaBaseResponse>() {
		}.getType());
	}

	public static String invoke(FujicaInterfaceEnum requestTypeEnums, String requestJson) {

		ResponseBody responseseBody = null;
		String entity = null;
		long start = System.currentTimeMillis();

		OkHttpClient client = new OkHttpClient();
		client.setConnectTimeout(15, TimeUnit.SECONDS);
		client.setReadTimeout(30, TimeUnit.SECONDS);
		time = getTimestamp();

		String url = FujicaInterfaceEnum.PROD_URL.getUrl() + requestTypeEnums.getUrl();
		Request request = new Request.Builder().url(url).post(RequestBody.create(MEDIATYPE_JSON, requestJson))
				.addHeader("appid", appid).addHeader("appSecret", appSecret).addHeader("sign", getSign(requestJson))
				.addHeader("timestamp", time).addHeader("content-type", "application/json")
				.addHeader("cache-control", "no-cache").build();
		for (int retryTime = 0; retryTime < 5; retryTime++) {
			try {
				Response httpResponse = client.newCall(request).execute();
				if (httpResponse != null && HttpStatus.SC_OK == httpResponse.code()) {
					responseseBody = httpResponse.body();
					if (responseseBody != null) {
						byte[] byteContent = httpResponse.body().bytes();
						entity = byteArrayToStr(byteContent);
						if (StringUtil.isValidString(entity)) {
							// return JSON.parseObject(entity, clazz);
							return entity;
						}
					}

				} else {
					log.error("调用富士停车接口，请求连接失败,状态码statusCode=" + httpResponse.code());
				}
			} catch (Exception e) {
				log.error("调用富士停车接口出现异常" + requestTypeEnums.getDesc(), e);
			} finally {
				log.info("第{}次 调用富士停车接口{},\n请求信息:{},\n响应信息：{} \n耗时：{}",retryTime, requestTypeEnums.getDesc(), requestJson, entity,
						(System.currentTimeMillis() - start));
				if (responseseBody != null) {
					try {
						responseseBody.close();
					} catch (IOException e) {
					}
				}
			}
		}

		return null;

	}
	
	

	public static String getTimestamp() {
		String time = "";
		long timestamp = System.currentTimeMillis();
		time = String.valueOf(timestamp);
		time = time.substring(0, 10);
		return time;
	}

	private static String getSign(String requestJson) {
		TreeMap<String, String> map = new TreeMap();
		map.put("param", requestJson);
		map.put("secret", appSecret);
		map.put("timestamp", time);
		sign = signWithoutMD5(map);
		sign = sign(sign, privateKey);
		return sign;
	}

	public static String signWithoutMD5(SortedMap<String, String> params) {
		String restul = "";
		if (params == null) {
			return restul;
		}

		StringBuilder sb = new StringBuilder();
		for (Map.Entry<String, String> me : params.entrySet()) {

			sb.append(me.getKey());
			sb.append('=');
			sb.append(me.getValue());
			sb.append('&');

		}
		int lastIndexOf = sb.lastIndexOf("&");
		if (lastIndexOf != -1) {
			sb.deleteCharAt(lastIndexOf);
		}
		restul = new String(sb);

		return restul;
	}

	public static String sign(String content, String privateKey) {
		try {
			PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decode(privateKey));
			KeyFactory keyf = KeyFactory.getInstance(ALGORITHM);

			PrivateKey priKey = keyf.generatePrivate(priPKCS8);

			java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);

			signature.initSign(priKey);
			signature.update(content.getBytes(DEFAULT_CHARSET));

			byte[] signed = signature.sign();

			return Base64.encode(signed);
		} catch (Exception e) {
			e.printStackTrace();
		}

		return null;
	}

	public static String byteArrayToStr(byte[] byteArray) throws UnsupportedEncodingException {
		if (byteArray == null) {
			return null;
		}
		String str = new String(byteArray,DEFAULT_CHARSET);
		return str;
	}
	 
	private static Gson gson =  new GsonBuilder().setDateFormat(DateUtils.PATTERN_DEFAULT_ON_SECOND).create();

	public static <T> T fromJson(String json, Type typeOfT) throws JsonSyntaxException {
		if (json == null) {
			return null;
		} else {
			// 泛型对象字段可能会传字符串""过来等
			json = json.replace("\"\"", "null");
			return gson.fromJson(json, typeOfT);
		}
	}
}
